/*
 * numeric.h
 *
 *  Created on: Feb 23, 2012
 *      Author: falko
 */

#ifndef NUMERIC_H_
#define NUMERIC_H_


#include <iostream>
#include <stdio.h>
#include <math.h>
#include <gmp.h>
#include <mpfr.h>



namespace falko
{

#define _TEST_
#ifdef _TEST_

#define TEST(str, result) std::cout<< #str  \
			<< ": " << result << std::endl
#else
#define TEST(str, result)
#endif

#define GMP_PREC 512
typedef unsigned int uint;

class hpf_t
{
public:
    mpfr_t val_;
    static const mpfr_rnd_t rounding_type_ = MPFR_RNDU;

    hpf_t();
    hpf_t(double val);
    hpf_t(const hpf_t& v);
    hpf_t(const char* v, uint base = 10);
    ~hpf_t()
    {
        mpfr_clear(val_);
    }

    hpf_t operator= (hpf_t const &v);
    hpf_t operator= (double v);
    hpf_t operator= (const char* v);
    hpf_t &operator+=(hpf_t const &v)
    {
        mpfr_add(val_, val_, v.val_, rounding_type_);
        return *this;
    }
    hpf_t &operator-=(hpf_t const &v)
    {
        mpfr_sub(val_, val_, v.val_, rounding_type_);
        return *this;
    }
    hpf_t &operator*=(hpf_t const &v)
    {
        mpfr_mul(val_, val_, v.val_, rounding_type_);
        return *this;
    }
    hpf_t &operator*=(double &v)
    {
        mpfr_mul(val_, val_, hpf_t(v).val_, rounding_type_);
        return *this;
    }
    hpf_t &operator/=(hpf_t const &v);
    hpf_t operator-() const;
    hpf_t operator+(hpf_t const &v) const;
    hpf_t operator-(hpf_t const &v) const;
    hpf_t operator*(hpf_t const &v) const;
    hpf_t operator*(double v) const;
    hpf_t operator/(hpf_t const &v) const;
    hpf_t operator/(uint v) const;
    hpf_t pow(uint v) const;
    hpf_t sqrt() const;
    hpf_t abs() const;
    bool operator==(hpf_t const &v) const;
    bool operator!=(hpf_t const &v) const;
    bool operator<(hpf_t const &v) const;
    bool operator<=(hpf_t const &v) const;
    bool operator>(hpf_t const &v) const;
    bool operator>=(hpf_t const &v) const;
    double toDouble() const;
    
};

void hpf_sin(hpf_t &rop, hpf_t const &op);
void hpf_cos(hpf_t &rop, hpf_t const &op);
//void hpf_tan(hpf_t rop, hpf_t const &op)
//{
//	mpfr_tan(rop.val_, op.val_, rop.rounding_type_);
//}

class hpv3d_t
{
public:
    hpf_t x_;
    hpf_t y_;
    hpf_t z_;

    hpv3d_t() {}
    hpv3d_t(hpv3d_t const &v);
    hpv3d_t(hpf_t const &x, hpf_t const &y, hpf_t const &z);
    ~hpv3d_t() {}
    hpv3d_t operator= (hpv3d_t const &v);
    hpv3d_t operator= (hpf_t const &v)
    {
        x_ = y_ = z_ = v;
        return *this;
    }
    hpv3d_t operator= (double v)
    {
        x_ = y_ = z_ = v;
        return *this;
    }
    hpv3d_t &operator+=(hpv3d_t const &v);
    hpv3d_t &operator-=(hpv3d_t const &v);
    hpv3d_t &operator%=(hpv3d_t const &v);
    hpv3d_t &operator*=(hpf_t const &v);
    hpv3d_t &operator/=(hpf_t const &v);
    hpv3d_t operator-() const;
    hpv3d_t operator+(hpv3d_t const &v) const;
    hpv3d_t operator-(hpv3d_t const &v) const;
    hpv3d_t operator%(hpv3d_t const &v) const;
    hpv3d_t operator*(hpf_t const &v) const;
    hpf_t operator*(hpv3d_t const &v) const;
    hpv3d_t operator/(hpf_t const &v) const;
    bool operator==(hpv3d_t const &v) const;
    bool operator!=(hpv3d_t const &v) const;

    void normalize();
    hpf_t magnitude() const;
};


class HpFileWriter
{
public:

    HpFileWriter() : out_file_(stdout), prec_(10) { }

    HpFileWriter(const char *file_name) : out_file_(NULL), prec_(10)
    {
        out_file_ = fopen(file_name, "w");
    }

    virtual ~HpFileWriter()
    {
        if (out_file_ != NULL && out_file_ != stdout)
            fclose(out_file_);
    }

    operator bool() const
    {
        return out_file_ != NULL;
    }

//    void setFile(const char *file_name)
//    {
//    	if (out_file_ != NULL && out_file_ != stdout)
//    		fclose(out_file_);
//    	out_file_ = fopen(file_name, "w");
//    }

    void setPrec(uint prec)
    {
        prec_ = prec;
    }

    void writeHpf(hpf_t const &v);
    void writeHpv3d(hpv3d_t const &v);
    void writeStr(const char *v);

private:
    FILE *out_file_;
    uint prec_;
};
HpFileWriter& operator<<(HpFileWriter& sout, hpf_t const &v);
HpFileWriter& operator<<(HpFileWriter& sout, hpv3d_t const &v);
HpFileWriter& operator<<(HpFileWriter& sout, const char *v);

} //End Falko


const falko::hpf_t _PI = "3.1415926535897932384626433832795028841971693993"
				  "751058209749445923078164062862089986280348253421"
				  "170679821480865132823066470938446095505822317253"
				  "594081284811174502841027019385211055596446229489"
				  "549303819644288109756659334461284756482337867831"
				  "65271201909145648566923460348610454326648213393607260249141";

#endif /* NUMERIC_H_ */
